package app.server.project.main.service.service.race;

import app.server.project.core.constants.Def;
import app.server.project.core.constants.TcpCmd;
import app.server.project.core.exception.SysError;
import app.server.project.core.http.CMSLstResp;
import app.server.project.core.websocket.WSMgr;
import app.server.project.main.service.bo.event.ClickEvent;
import app.server.project.main.service.bo.event.RechargeSuccessEvent;
import app.server.project.main.service.db.dao.cfg.RaceCfgDao;
import app.server.project.main.service.db.dao.user.RaceClickDao;
import app.server.project.main.service.db.dao.user.UserExtLock;
import app.server.project.main.service.db.dao.user.UserInfoDao;
import app.server.project.main.service.db.dao.user.UserRaceDao;
import app.server.project.main.service.db.entity.cfg.RaceCfgEntity;
import app.server.project.main.service.db.entity.user.RaceClickEntity;
import app.server.project.main.service.db.entity.user.UserInfoEntity;
import app.server.project.main.service.dto.FreeDto;
import app.server.project.main.service.dto.cms.CoinDto;
import app.server.project.main.service.dto.race.EarnReq;
import app.server.project.main.service.dto.race.RaceInfoDto;
import app.server.project.main.service.service.EnergyService;
import app.server.project.main.service.service.LockService;
import app.server.project.main.service.service.ResService;
import app.server.project.main.service.service.lv.UserLvService;
import app.server.project.main.service.service.recharge.CallbackType;
import cn.hutool.core.util.RandomUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

@Service
@Slf4j
@AllArgsConstructor
public class RaceService {


    private UserRaceDao userRaceDao;
    private RaceCfgDao raceCfgDao;
    private UserInfoDao userInfoDao;
    private UserLvService userLvService;
    private EnergyService energyService;
    private ResService resService;
    private LockService lockService;
    private RaceClickDao raceClickDao;
    private UserExtLock extLock;
    private ApplicationEventPublisher applicationEventPublisher;

    public List<RaceInfoDto> getRaceInfo(long userId) {
        var cfg = this.raceCfgDao.getAll();
        var userNums = this.userRaceDao.getRaceInfo(userId);
        List<RaceInfoDto> resp = new ArrayList<>();
        Date now = new Date();
        for (int i = 0; i < cfg.size(); i++) {
            RaceClickEntity raceClickEntity = this.raceClickDao.getByRaceId(cfg.get(i).getId());
            var userRaceEntity = this.userRaceDao.getOne(userNums, cfg.get(i).getId(), userId);
            userRaceEntity.resetFreeNum(cfg.get(i));
            if (cfg.get(i).getId() == this.raceCfgDao.getAiCoin().getId()) {
                var userInfo = this.userInfoDao.getUserInfoById(userId);
                resp.add(RaceInfoDto.ofAiCoin(cfg.get(i),
                        userInfo.getAiCoin(),
                        raceClickEntity.getClickNum(),
                        userRaceEntity.getActivityFreeNumByCfg(cfg.get(i))));
            } else {
                resp.add(RaceInfoDto.of(cfg.get(i), userRaceEntity, raceClickEntity.getClickNum()));
            }
        }
        return resp;
    }


    public CMSLstResp<CoinDto> getCoinLstByUserId(long userId) {
        var dbs = this.userRaceDao.getRaceInfo(userId);
        var user = this.userInfoDao.getUserInfoById(userId);
        var cogs = this.raceCfgDao.getAll();

        cogs.sort(Comparator.comparingInt(RaceCfgEntity::getOrderIndex));


        var ret = new ArrayList<CoinDto>(cogs.stream().map(v -> {
                    var db = dbs.stream().filter(f -> f.getRaceId() == v.getId()).findFirst();
                    var mapRet = CoinDto.of(db.orElse(null), v, userId);
                    if (v.getId() == this.raceCfgDao.getAiCoin().getId()) {
                        mapRet.setVal(user.getAiCoin());
                    }
                    if (mapRet.getVal() == Def.Zero) {
                        mapRet.setVal(Def.Zero);
                    }
                    return mapRet;
                })
                .toList());
        return CMSLstResp.of(ret, ret.size());
    }

    /**
     * 检查赛道解锁
     *
     * @param req
     * @param userId
     */
    private boolean chk(EarnReq req, long userId) {
        UserInfoEntity userInfoEntity = this.userInfoDao.getUserInfoById(userId);
        this.userLvService.chkAndInitLv(userId);
        RaceCfgEntity cfgEntity = this.raceCfgDao.getOne(req.getRaceId());
        //如果充值过,就不需要检查了
        var lst = this.userRaceDao.getRaceInfo(userId);
        var db = this.userRaceDao.getOne(lst, req.getRaceId(), userId);
        var ext = this.extLock.getUserExtByUserExtId(userId);
        //解锁了全部赛道,不需要检查
        if (ext.getUnlockRaceAll() == Def.True) return true;
        //解锁了指定赛道,不需要检查
        if (db.getTonLock() == Def.True) return true;
        //买了点击次数
        if (ext.getPropClickNum() > Def.Zero) return true;
        //检查活动是否开启
        if (!db.canClick(cfgEntity)) {
            //活动不开启,开始检查等级,是否解锁
            //开始检查等级
            if (cfgEntity.getLvLock() > userInfoEntity.getLv()) {
                WSMgr.pushError(userId, TcpCmd.Error, SysError.Lv);
                return false;
            }
        }
        return true;
    }

    private void clickRace(EarnReq req, long userId) {
        RaceCfgEntity cfgEntity = this.raceCfgDao.getOne(req.getRaceId());
        var randomInt = RandomUtil.randomInt(0, 101);
        if (randomInt > cfgEntity.getChance()) {
            //开始检查爆率作用
            var ext = this.extLock.getUserExtByUserExtId(userId);
            if (Def.Zero >= ext.getChanceTimeForSeconds()) {
                //爆率 不发生作用
                WSMgr.push(userId, TcpCmd.HitRate, false);
                return;
            }
        }
        long num = cfgEntity.getProduce();
        WSMgr.push(userId, TcpCmd.Hit, req);
        WSMgr.push(userId, TcpCmd.HitRate, true);
        this.resService.addResNum(userId, req.getRaceId(), num, false);
        //this.applicationEventPublisher.publishEvent(AddRaceValEvent.of(userId, cfgEntity, num, DetailType.Click));
    }


    /**
     * 解锁赛道
     *
     * @param
     */
    @EventListener(RechargeSuccessEvent.class)
    public void unLockRace(RechargeSuccessEvent event) {
        if (event.getCallbackType() != CallbackType.Race) return;
        log.info("用户:{} 充值成功开始解锁赛道:{}", event.getUserId(), event.getParam());
        if (event.getParam() == Def.Zero) {
            var ext = this.extLock.getUserExtByUserExtId(event.getUserId());
            ext.setUnlockRaceAll(Def.True);
            WSMgr.push(event.getUserId(),
                    TcpCmd.UnLockRaceAll,
                    Def.True);
        } else {
            WSMgr.push(event.getUserId(),
                    TcpCmd.UnLockRace,
                    event.getParam());
            WSMgr.push(event.getUserId(), TcpCmd.RechargeSuccessRace, event.getParam());
            var lst = this.userRaceDao.getRaceInfo(event.getUserId());
            var one = this.userRaceDao.getOne(lst, (int) event.getParam(),
                    event.getUserId());
            one.setTonLock(Def.True);
        }

    }

    @EventListener(RechargeSuccessEvent.class)
    public void onPropClick(RechargeSuccessEvent event) {
        if (event.getCallbackType() != CallbackType.Click) return;
        log.info("用户:{} 充值成功开始添加点击次数:{}", event.getUserId(), event.getParam());
        var ext = this.extLock.getUserExtByUserExtId(event.getUserId());
        ext.addPropClickNum(event.getParam());
        WSMgr.push(event.getUserId(), TcpCmd.PropClickNum, ext.getPropClickNum());
        WSMgr.push(event.getUserId(), TcpCmd.PropClickNumSuccess, null);
    }

    @EventListener(RechargeSuccessEvent.class)
    public void onChanceTime(RechargeSuccessEvent event) {
        if (event.getCallbackType() != CallbackType.Rate) return;
        log.info("用户:{} 充值成功开始添加爆率作用时间:{}", event.getUserId(), event.getParam());
        var ext = this.extLock.getUserExtByUserExtId(event.getUserId());
        if (Objects.isNull(ext.getChanceTime())) {
            ext.setChanceTime(LocalDateTime.now());
        }
        if (ext.getChanceTime().isBefore(LocalDateTime.now())) {
            ext.setChanceTime(LocalDateTime.now());
        }
        ext.addChanceTime(event.getParam());
        WSMgr.push(event.getUserId(), TcpCmd.ChanceTime, ext.getChanceTimeForSeconds());
        WSMgr.push(event.getUserId(), TcpCmd.ChanceTimeSuccess, null);
    }

    @Async(Def.CommonPool)
    public void earnCoin(EarnReq req, long userId) {
        var lock = this.lockService.getLock(userId);
        synchronized (lock) {
            if (!this.chk(req, userId)) {
                //WSMgr.push(userId, TcpCmd.Hit, req);
                return;
            }
            var list = this.userRaceDao.getRaceInfo(userId);
            var one = this.userRaceDao.getOne(list, req.getRaceId(), userId);

            RaceCfgEntity cfgEntity = this.raceCfgDao.getOne(req.getRaceId());
            one.resetFreeNum(cfgEntity);
            int clickNum = 0;
            for (int i = 0; i < 1; i++) {
                boolean consumeFlag = true;
                if (one.getActivityFreeNumByCfg(cfgEntity) > 0) {
                    //处理免费点击次数
                    one.addActivityFreeNum();
                    WSMgr.push(userId, TcpCmd.Free, FreeDto.of(req.getRaceId(), one.getActivityFreeNumByCfg(cfgEntity)));
                } else {
                    //优先使用用户体力
                    consumeFlag = this.energyService.consume(userId, cfgEntity.getConsumeEnergy(), false);
                    if (!consumeFlag) {
                        //开始消耗用户点击次数
                        var ext = this.extLock.getUserExtByUserExtId(userId);
                        if (ext.getPropClickNum() > Def.Zero) {
                            ext.consumePropClickNum();
                            WSMgr.push(userId, TcpCmd.PropClickNum, ext.getPropClickNum());
                            consumeFlag = true;
                        }
                    }
                }

                if (!consumeFlag) {
                    WSMgr.pushError(userId, TcpCmd.Error, SysError.Energy);

                    this.energyService.pushEnergy(userId);
                    //WSMgr.push(userId, TcpCmd.Hit, req);
                    return;
                }
                if (Def.True == req.getHit()) {
                    this.clickRace(req, userId);
                } else {
                    WSMgr.push(userId, TcpCmd.Hit, req);
                }
                clickNum++;
            }

            //发送体力值变化
            this.energyService.pushEnergy(userId);
            //发送点击事件
            this.applicationEventPublisher.publishEvent(ClickEvent.of(userId, clickNum, req.getRaceId()));
            if (Def.True == req.getHit()) {
                var aiCfg = this.raceCfgDao.getAiCoin();
                //如果是ai coin,推送ai coin 变化
                if (aiCfg.getId() == req.getRaceId()) {
                    this.resService.pushAiCoin(userId);
                } else {

                    this.resService.pushRaceValDto(userId, one);
                }
            }
        }
    }
}
